/*
 * This file is part of Awake FILE. 
 * Awake file: Easy file upload & download over HTTP with Java.                                    
 * Copyright (C) 2015,  KawanSoft SAS
 * (http://www.kawansoft.com). All rights reserved.                                
 *                                                                               
 * Awake FILE is free software; you can redistribute it and/or                 
 * modify it under the terms of the GNU Lesser General Public                    
 * License as published by the Free Software Foundation; either                  
 * version 2.1 of the License, or (at your option) any later version.            
 *                                                                               
 * Awake FILE is distributed in the hope that it will be useful,               
 * but WITHOUT ANY WARRANTY; without even the implied warranty of                
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU             
 * Lesser General Public License for more details.                               
 *                                                                               
 * You should have received a copy of the GNU Lesser General Public              
 * License along with this library; if not, write to the Free Software           
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  
 * 02110-1301  USA
 *
 * Any modifications to this file must keep this entire header
 * intact.
 */
package org.kawanfw.file.reflection;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.NotSerializableException;
import java.lang.reflect.Modifier;
import java.net.ConnectException;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.logging.Level;

import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.kawanfw.commons.api.client.InvalidLoginException;
import org.kawanfw.commons.api.client.RemoteException;
import org.kawanfw.commons.server.util.ServerLogger;
import org.kawanfw.commons.util.FrameworkDebug;
import org.kawanfw.commons.util.FrameworkSystemUtil;
import org.kawanfw.commons.util.Tag;
import org.kawanfw.file.api.client.RemoteFile;
import org.kawanfw.file.api.client.RemoteSession;

/**
 * 
 * Methods to upload FileFilter & FilenameFilter
 * 
 * @author Nicolas de Pomereu
 */
public class FilterUploader {

    public static boolean ALLOW_ANDROID_FILTER_UPLOAD = true;
    
    private static boolean DEBUG = FrameworkDebug.isSet(FilterUploader.class);

    /** The Remote File on which to execute filter uploading */
    private RemoteFile remoteFile = null;

    /**
     * Constructors
     * 
     * @param remoteFile
     *            remote file for which we wat to upload FileFilter &
     *            FilenameFilter
     */
    public FilterUploader(RemoteFile remoteFile) {
	this.remoteFile = remoteFile;
    }

    /**
     * Localise the .class of a FilenameFilter or FileFilter and upload it.
     * 
     * @param clazz
     *            the FilenameFilter or FilerFilter class to upload
     * @param callerClassName
     *            name of the caller
     * 
     * @return the uploaded file name
     * 
     * @throws NotSerializableException
     * @throws IOException
     * @throws IllegalArgumentException
     * @throws InvalidLoginException
     * @throws FileNotFoundException
     * @throws UnknownHostException
     * @throws ConnectException
     * @throws SocketException
     * @throws RemoteException
     */
    public String uploadFilterClassFile(Class<?> clazz, String callerClassName)
	    throws NotSerializableException, IOException,
	    IllegalArgumentException, InvalidLoginException,
	    FileNotFoundException, UnknownHostException, ConnectException,
	    SocketException, RemoteException {

	if (FrameworkSystemUtil.isAndroid() && !ALLOW_ANDROID_FILTER_UPLOAD) {
	    
	    if (ReflectionUtil.isClassFilenameFilter(clazz)) {
		throw new IllegalArgumentException(Tag.PRODUCT
			+ " Invalid FilenameFilter. FilenameFilter on Android "
			+ "must extend KawanfwFilenameFilter: "
			+ clazz.getName());
	    }
	    else if (ReflectionUtil.isClassFileFilter(clazz)) {
		throw new IllegalArgumentException(Tag.PRODUCT
			+ " Invalid FileFilter. FileFilter on Android "
			+ "must extend KawanfwFileFilter: "
			+ clazz.getName());
	    }
	    else {
		throw new IllegalArgumentException(Tag.PRODUCT_PRODUCT_FAIL + " class is not a FileFilter nor a FilenameFilter: " + clazz.getName());
	    }
	}
	
	try {
	    String modifiers = Modifier.toString(clazz.getModifiers());
	    if (!modifiers.contains("static") || !modifiers.contains("public")) {
		clazz.newInstance();
	    }
	} catch (Exception e) {
	    if (e instanceof InstantiationException) {
		throw new IllegalArgumentException(
			Tag.PRODUCT
				+ " Invalid FilenameFilter or FileFilter. Filter class must have a nullary constructor: "
				+ clazz.getName());
	    } else if (e instanceof IllegalAccessException) {
		throw new IllegalArgumentException(
			Tag.PRODUCT
				+ " Invalid FilenameFilter or FileFilter. Filter inner class must be static and public: "
				+ clazz.getName());
	    } else {
		throw new IllegalArgumentException(
			Tag.PRODUCT
				+ " Invalid FilenameFilter or FileFilter. Filter class can not be invoked with new(classname) : "
				+ clazz.getName());
	    }
	}

	RemoteFile remoteFile = new RemoteFile(
		this.remoteFile.getRemoteSession(), "/.classes");
	remoteFile.mkdir();

	// Find the .class file of jar file
	ClassFileLocatorNew classFileLocator = new ClassFileLocatorNew(clazz,
		callerClassName);

	File filterFile = null;

	try {
	    filterFile = classFileLocator.exportClassToTempFile();

	    String filterFilename = StringUtils.substringAfterLast(
		    clazz.getName(), ".")
		    + ".class";

	    String classSubdirectories = "/.classes"
		    + ClassFileLocatorNew.getClassSubdirectories(clazz
			    .getName());

	    remoteFile = new RemoteFile(this.remoteFile.getRemoteSession(),
		    classSubdirectories);
	    remoteFile.mkdir();

	    if (!classSubdirectories.endsWith("/")) {
		classSubdirectories += "/";
	    }

	    filterFilename = classSubdirectories + filterFilename;

	    debug("filterFilename: " + filterFilename);
	    debug("filterFile    : " + filterFile);

	    RemoteSession remoteSession = this.remoteFile.getRemoteSession();
	    remoteSession.upload(filterFile, filterFilename);

	    return filterFilename;
	} finally {
	    FileUtils.deleteQuietly(filterFile);
	}
    }

    private void debug(String s) {
	if (DEBUG) {
	    ServerLogger.getLogger().log(Level.WARNING, s);
	}
    }

}
